<template>
  <div id="container" @click="hideContextMenu">
    <!-- 任务列表横向排列在最上方，同时任务标签支持右键弹出菜单 -->
    <div id="taskContainer">
      <div id="taskTabs">
  <span
      class="task-tab"
      :class="{ active: currentTaskIndex === null }"
      @click="switchTask(null)"
  >
    主界面查看
  </span>
        <span
            v-for="(task, index) in activeTasks"
            :key="task.id"
            class="task-tab"
            :class="{ 'active-tab': currentTaskIndex === index, 'flash': task.flash, 'current-task': currentTaskIndex === index }"
            @click.stop="switchTask(index)"
            @contextmenu.prevent.stop="showContextMenu($event, index)"
        >
    {{ task.name }} ({{ formatTaskTime(task.created_at) }})
    <span v-if="task.progress < 100"> ({{ task.progress }}%)</span>
    <span class="delete-icon" @click.stop="showDeleteConfirm($event, index, false)">×</span>
</span>
      </div>
    </div>

    <!-- 自定义右键菜单，显示在鼠标点击位置 -->
    <div
        v-if="contextMenuVisible"
        class="context-menu"
        :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }"
    >
      <!-- 菜单项：删除任务 -->
      <div class="context-menu-item" @click="showDeleteConfirm">
        删除任务
      </div>
    </div>

    <div
        v-if="deleteConfirmVisible"
        class="delete-confirm"
        :style="{ top: deleteConfirmY + 'px', left: deleteConfirmX + 'px' }"
    >
      <div class="delete-confirm-message">
        确认删除任务 {{ deleteTaskName }} 吗？
      </div>
      <div class="delete-confirm-buttons">
        <button @click="confirmDeleteTask">确认</button>
        <button @click="cancelDeleteTask">取消</button>
      </div>
    </div>

    <!-- 首页右侧历史任务列表，点击按钮显示/隐藏 -->
    <div id="historyContainer">
      <!-- 切换历史任务列表的显示状态 -->
      <button id="toggleHistory" @click="showHistoryTasks = !showHistoryTasks">
        {{ showHistoryTasks ? '隐藏历史任务' : '显示历史任务' }}
      </button>
      <!-- 历史任务列表，点击任务可重新加载 -->
      <div id="historyTaskList" v-if="showHistoryTasks">
        <h3>历史任务</h3>
        <ul>
          <!-- 动态渲染历史任务 -->
          <li
              v-for="(task, index) in historyTasks"
              :key="task.id"
              class="history-task-item"
              @click="historyTaskClick(task.id)"
              @contextmenu.prevent.stop="showContextMenu($event, index, true)"
          >
            {{ task.name }} ({{ formatHistoryTaskDate(task.created_at) }})
            <span class="delete-icon" @click.stop="showDeleteConfirm($event, index, true)">×</span>
          </li>
        </ul>
      </div>
    </div>

    <!-- 主界面：允许用户编辑关系图和指标值 -->
    <div v-if="!taskViewMode">
      <!-- 预测时间输入框，设置预测的月数 -->
      <div id="monthInputLegend">
        <label for="forecast-month-input">预测时间（月）：</label>
        <input type="number" id="forecast-month-input" v-model.number="forecastMonth" min="1" max="60" /> 个月
      </div>
      <!-- 关系图区域 -->
      <div id="relationSection">
        <!-- 颜色图例 -->
        <div id="categoryLegend">
          <div v-for="(category, index) in categories" :key="category.name" class="category-legend-item">
            <span class="legend-color" :style="{ backgroundColor: category.color || '#1E90FF' }"></span>
            <span class="legend-text">{{ category.name }}</span>
          </div>
        </div>
        <!-- 边控制区域，仅在未锁定状态下显示 -->
        <div id="edgeControls" v-if="!relationLocked">
          <div class="edge-control-wrapper">
            <!-- 添加边：选择起点和终点，点击确认添加 -->
            <div class="edge-add">
              <label>添加边</label>
              <select v-model="addEdge.from">
                <option disabled value="">请选择起点</option>
                <option v-for="node in nodes" :key="node.id" :value="node.id">{{ node.name }}</option>
              </select>
              <span>→</span>
              <select v-model="addEdge.to">
                <option disabled value="">请选择终点</option>
                <option v-for="node in nodes" :key="node.id" :value="node.id" v-if="node.id !== addEdge.from">{{ node.name }}</option>
              </select>
              <button @click="confirmAddEdge">确认添加</button>
            </div>
            <!-- 删除边：选择起点和终点，点击确认删除 -->
            <div class="edge-remove">
              <label>删除边</label>
              <select v-model="removeEdge.from" @change="updateRemoveOptions">
                <option disabled value="">请选择起点</option>
                <option v-for="node in nodes" :key="node.id" :value="node.id">{{ node.name }}</option>
              </select>
              <span>→</span>
              <select v-model="removeEdge.to">
                <option disabled value="">请选择终点</option>
                <option v-for="node in removeOptions" :key="node.id" :value="node.id">{{ node.name }}</option>
              </select>
              <button @click="confirmRemoveEdge">确认删除</button>
            </div>
          </div>
        </div>
        <!-- 关系图容器，用于渲染 ECharts 图表 -->
        <div id="relationChart" style="width: 100%; height: 600px;"></div>
        <!-- 锁定按钮：锁定关系图后不可编辑 -->
        <div v-if="!relationLocked">
          <button id="lock-relation" @click="lockRelationChart">指标因果网络确定</button>
        </div>
        <!-- 已锁定提示 -->
        <div v-else>
          <span style="color:green;font-weight:bold;">指标因果网络已锁定</span>
        </div>
        <!-- 操作按钮：解除锁定和重置网络 -->
        <div class="relation-actions">
          <button id="unlock-relation" @click="unlockRelationChart" :disabled="!areAllTasksComplete">解除锁定</button>
          <button id="reset-network" @click="resetNetwork" :disabled="!canResetNetwork">加载初始网络</button>
        </div>
      </div>
      <!-- 图例区域：显示指标值和变化 -->
      <div id="topLegend">
        <!-- 动态渲染图例项 -->
        <div v-for="node in legendNodes" :key="node.id" class="legend-item">
          <div class="legend-cell" :class="getNodeTextColor(node)">{{ node.name }}</div>
          <div class="legend-cell input-wrapper">
            <!-- 指标值输入框，锁定后可编辑 -->
            <input
                type="number"
                step="0.01"
                :value="node.indicatorValue.toFixed(2)"
                @change="handleValueChange(node, $event)"
                :disabled="!relationLocked"
                class="no-spinners"
            />
            <!-- 自定义上下箭头按钮 -->
            <div class="spinner-buttons" v-if="relationLocked">
              <button
                  class="spinner-up"
                  @click="adjustValue(node, 1)"
                  :disabled="!relationLocked"
              >▲</button>
              <button
                  class="spinner-down"
                  @click="adjustValue(node, -1)"
                  :disabled="!relationLocked"
              >▼</button>
            </div>
          </div>
          <!-- 显示变化率 -->
          <div class="legend-cell">{{ node.possibility >= 0 ? '+' : '' }}{{ node.possibility.toFixed(2) }}%</div>
          <!-- 显示值变化 -->
          <div class="legend-cell" :class="getNodeTextColor(node)">{{ node.previousValue.toFixed(2) + ' → ' + node.indicatorValue.toFixed(2) }}</div>
        </div>
        <!-- 提交任务按钮 -->
        <div id="legendActions">
          <button @click="generateTask" :disabled="!relationLocked || !areAllTasksComplete">提交任务</button>
          <button @click="resetEvidence" :disabled="!relationLocked || !areAllTasksComplete">重置证据</button>
        </div>
      </div>
    </div>

    <!-- 任务界面：只读模式，显示任务快照 -->
    <div v-if="taskViewMode">
      <!-- 模块1：显示锁定时的关系图 -->
      <div id="lockedRelationSection">
        <!-- 颜色图例 -->
        <div id="categoryLegend">
          <div v-for="category in categories" :key="category.name" class="category-legend-item">
            <span class="legend-color" :style="{ backgroundColor: category.color }"></span>
            <span class="legend-text">{{ category.name }}</span>
          </div>
        </div>
        <div v-if="!lockedRelationNodes.length" class="no-data">指标因果网络数据暂不可用</div>
        <div id="lockedRelationChart" style="width: 100%; height: 600px;"></div>
        <span style="color:green;font-weight:bold;">指标因果网络已锁定</span>
      </div>
      <!-- 模块2：阶数选择及图例数据 -->
      <div id="orderControls">
        <label for="order-select">选择阶数：</label>
        <!-- 选择传播阶数，触发数据更新 -->
        <select id="order-select" v-model="selectedOrder" @change="updateDataByOrder">
          <option value="0">0阶</option>
          <option value="1">第一阶</option>
          <option value="2">第二阶</option>
          <option value="3">第三阶</option>
        </select>
      </div>
      <!-- 任务界面的图例数据 -->
      <div id="taskLegend">
        <div v-for="node in legendNodes" :key="node.id" class="legend-item">
          <div class="legend-cell" :class="getNodeTextColor(node)">{{ node.name }}</div>
          <div class="legend-cell">
            <!-- 只读模式的指标值 -->
            <input type="number" :value="node.indicatorValue.toFixed(2)" disabled />
          </div>
          <div class="legend-cell">{{ node.possibility >= 0 ? '+' : '' }}{{ node.possibility.toFixed(2) }}%</div>
          <div class="legend-cell" :class="getNodeTextColor(node)">{{ node.previousValue.toFixed(2) + ' → ' + node.indicatorValue.toFixed(2) }}</div>
        </div>
      </div>
      <!-- 模块3：隐藏模块 -->
      <div>
        <!-- 切换隐藏模块的显示状态 -->
        <button @click="toggleHiddenModule">
          {{ hiddenModuleVisible ? "展示/隐藏推演指标结果" : "展示/隐藏推演指标结果" }}
        </button>
      </div>
      <!-- 隐藏模块：显示指标的折线图 -->
      <div v-if="hiddenModuleVisible">
        <div id="hidden-module">
          <div id="hidden-module-header">
            <label for="month-select-hidden">后预测月数：</label>
            <!-- 选择预测月数，更新折线图 -->
            <select
                id="month-select-hidden"
                v-model="selectedForecastMonth"
                @change="updateHiddenModuleCharts"
            >
              <option v-for="month in forecastMonth" :key="month" :value="month">
                {{ month }}
              </option>
            </select>
            <span>个月指标</span>
          </div>
          <!-- 指标折线图网格 -->
          <div id="data-grid">
            <div v-for="node in legendNodes" :key="node.id" class="grid-item">
              <div>{{ node.name }}</div>
              <div :id="'miniChart' + node.id" style="width: 100%; height: 150px;"></div>
            </div>
          </div>
        </div>
      </div>
      <!-- 模块4：两个折线图 -->
      <div id="lineChart-controls">
        <label for="month-select-chart">选择月份：</label>
        <!-- 选择显示的预测月份，更新折线图 -->
        <select id="month-select-chart" v-model="selectedMonth" @change="initializeLineCharts">
          <option v-for="month in forecastMonth" :key="month" :value="month">
            {{ month }}月
          </option>
        </select>
      </div>
      <!-- 折线图容器：系统性风险指数和上证指数 -->
      <div id="lineChart-container">
        <div id="lineChart1" style="width: 48%; height: 400px;"></div>
        <div id="lineChart2" style="width: 48%; height: 400px;"></div>
      </div>
    </div>
  </div>
</template>

<script>
import * as echarts from "echarts";
import http from "@/utils/request";
import { v4 as uuidv4 } from 'uuid';

const NAME_MAPPING = {
  B_Financial_P_GR: '银行理财产品发行数量',
  B_Financial_P_3M_GR: '银行理财产品发行数量近 3 月涨跌幅',
  B_Financial_P_6M_GR: '银行理财产品发行数量近 6 月涨跌幅',
  B_Financial_P_change_GR: '银行理财产品发行数量涨跌幅',
  boc_mean_GR: '人民币-美元汇率中间价月度均值',
  boc_median_GR: '人民币-美元汇率中间价月度中位数',
  ch_GDP_GR: '中国 GDP',
  ch_n_pmi_GR: '中国非制造业 PMI',
  ch_pmi_GR: '中国制造业 PMI',
  ch_pmi_YOY: '中国制造业同比增长',
  ch_ppi_GR: '中国 PPI',
  ch_shrzgm_GR: '社会融资规模增量',
  CZSR_GR: '财政收入',
  CZSR_YOY: '财政收入同比增长',
  D_deposits_GR: '活期存款',
  S_deposits_GR: '储蓄存款',
  F_credit_GR: '新增信贷数',
  F_credit_YOY: '新增信贷数同比增长',
  F_exchange_GR: '央行货币当局资产负债外汇',
  M_gold_GR: '央行货币当局资产负债货币黄金',
  FDD_dnbs_GR: '金融部门负债方杠杆率',
  FDZ_cnbs_GR: '金融部门资产方杠杆率',
  'JM-cnbs_GR': '居民部门杠杆率',
  LG_cnbs_GR: '地方政府杠杆率',
  ST_cnbs_GR: '实体经济部门杠杆率',
  NF_cnbs_GR: '非金融企业部门杠杆率',
  wbck_GR: '本外币存款',
  wbck_YOY: '本外币存款同比增长',
  LPR1Y_GR: 'LPR_1Y 利率',
  RATE_1_GR: '短期贷款利率',
  M0_GR: '流通中现金',
  M0_YOY: '流通中现金同比增长',
  M1_GR: '狭义货币',
  M1_YOY: '狭义货币同比增长',
  M2_GR: '广义货币',
  M2_YOY: '广义货币 M2 同比增长',
  shbor_GR: '上海银行同业拆借市场隔夜拆借利率月度中位数',
  shbor_mean_GR: '上海银行同业拆借市场隔夜拆借利率月度均值',
  us_cpi_GR: '美国 CPI',
  us_gdp_GR: '美国 GDP',
  us_pmi_GR: '美国制造业 PMI',
  us_rate_GR: '美元利率',
  libor_mean_GR: '伦敦银行间美元利率月均值',
  libor_median_GR: '伦敦银行间美元利率月中位数'
};

const REVERSE_NAME_MAPPING = Object.fromEntries(
    Object.entries(NAME_MAPPING).map(([en, cn]) => [cn, en])
);

const DEFAULT_CATEGORIES = [
  { name: "国内经济指标", color: "#1E90FF" },
  { name: "美国经济指标", color: "#FF4500" },
  { name: "重要指标", color: "#FFD700" }
];

export default {
  data() {
    return {
      categories: [
        { name: "国内经济指标", color: "#1E90FF" },
        { name: "美国经济指标", color: "#FF4500" },
        { name: "重要指标", color: "#FFD700" }
      ],
      userId: '', // 动态获取真实用户ID
      forecastMonth: 12,
      taskViewMode: false,
      selectedOrder: 0,
      selectedMonth: 12,
      hiddenModuleVisible: false,
      selectedForecastMonth: 12,
      showHistoryTasks: false,
      taskCounter: 0,
      mainNodes: [],
      mainLinks: [],
      nodes: [],
      links: [],
      legendNodes: [],
      lockedRelationNodes: [],
      // categories: DEFAULT_CATEGORIES,
      addEdge: { from: "", to: "" },
      removeEdge: { from: "", to: "" },
      removeOptions: [],
      relationLocked: false,
      relationModified: false,
      activeTasks: [],
      historyTasks: [],
      currentTaskIndex: null,
      contextMenuVisible: false,
      isHistoryTask: false,
      // 新增：删除确认提示框相关数据
      deleteConfirmVisible: false,
      deleteConfirmX: 0,
      deleteConfirmY: 0,
      deleteTaskName: '',
      taskStatusInterval: null,
      contextMenuX: 0,
      contextMenuY: 0,
      contextMenuTaskIndex: null,
      relationChartInstance: null,
      validNodeNames: new Set(),
      globalMin: Infinity,
      globalMax: -Infinity,
      hiddenDates: [],
      hiddenSeries: [],
      lineChart1Instance: null,
      lineChart2Instance: null,
      legendData: {} // 新增存储 /relation/BN/ 的 data 字段
    };
  },

  computed: {
    areAllTasksComplete() {
      return this.activeTasks.length === 0 || this.activeTasks.every(task => task.progress === 100);
    },
    canResetNetwork() {
      return this.areAllTasksComplete && !this.relationLocked;
    }
  },

  watch: {
    selectedOrder(newVal, oldVal) {
      console.log(`selectedOrder changed from ${oldVal} to ${newVal}`);
    }
  },

  async mounted() {
    // 验证 categories 数据
    console.log('Categories initialized:', this.categories);
    if (!this.categories.every(cat => cat.color && typeof cat.color === 'string')) {
      console.warn('Categories contain invalid color values, resetting to default');
      this.$set(this, 'categories', [
        { name: "国内经济指标", color: "#1E90FF" },
        { name: "美国经济指标", color: "#FF4500" },
        { name: "重要指标", color: "#FFD700" }
      ]);
    }
    // 从 sessionStorage 读取 userId
    this.userId = sessionStorage.getItem('user') || 'default_user'; // 如果未找到，使用默认值
    if (!this.userId || this.userId === 'default_user') {
      console.warn('未找到 userId，请确保用户已登录');
      // 可选：跳转到登录页面
      // this.$router.push('/login');
    }

    await this.initializeData();
    this.setupEventListeners();
    window.addEventListener('resize', this.resizeChart);
    this.startTaskStatusPolling();
    // 强制刷新图例
    this.$nextTick(() => {
      this.renderCategoryLegend();
    });
  },

  beforeDestroy() {
    this.cleanupEventListeners();
    window.removeEventListener('resize', this.resizeChart);
    if (this.relationChartInstance) {
      this.relationChartInstance.dispose();
      this.relationChartInstance = null;
    }
    this.stopTaskStatusPolling();
  },

  methods: {
    // 强制渲染图例
    renderCategoryLegend() {
      console.log('Rendering category legend with categories:', this.categories);
      this.$forceUpdate(); // 强制更新 DOM
      // 确保 DOM 就绪后重新应用样式
      const legendColors = document.querySelectorAll('.legend-color');
      legendColors.forEach(color => {
        const bgColor = color.style.backgroundColor;
        if (!bgColor || bgColor === '') {
          console.warn('Legend color not applied, forcing default:', color);
          color.style.backgroundColor = '#1E90FF';
        }
      });
    },

    renderRelationChart() {
      const container = document.getElementById("relationChart");
      if (!container) {
        console.error('未找到指标因果网络容器 #relationChart');
        return;
      }

      if (this.relationChartInstance) {
        this.relationChartInstance.dispose();
      }
      this.relationChartInstance = echarts.init(container);

      // 验证 categories 数据
      if (!this.categories.every(cat => cat.color && typeof cat.color === 'string')) {
        console.warn('Invalid categories detected, resetting to default');
        this.$set(this, 'categories', [
          { name: "国内经济指标", color: "#1E90FF" },
          { name: "美国经济指标", color: "#FF4500" },
          { name: "重要指标", color: "#FFD700" }
        ]);
      }

      const nodesToRender = this.nodes.length > 0 ? this.nodes : [{
        id: "placeholder",
        name: "无数据",
        symbolSize: 50,
        category: 0,
        indicatorValue: 0,
        possibility: 0,
        previousValue: 0
      }];

      const linksToRender = this.links.length > 0 ? this.links : [];

      const option = {
        tooltip: {
          formatter: params => `
            <div style="padding:5px;">
              <strong>${params.data.name}</strong><br/>
              当前值: ${parseFloat(params.data.indicatorValue).toFixed(2)}<br/>
              变化率: ${params.data.possibility >= 0 ? '+' : ''}${parseFloat(params.data.possibility).toFixed(2)}%<br/>
              变化: ${parseFloat(params.data.previousValue).toFixed(2)} → ${parseFloat(params.data.indicatorValue).toFixed(2)}
            </div>`,
          extraCssText: "text-align: left;"
        },
        series: [{
          type: "graph",
          layout: "force",
          edgeSymbol: ['none', 'arrow'],
          edgeSymbolSize: [4, 10],
          force: {
            repulsion: 500,
            edgeLength: [50, 150],
            gravity: 0.05
          },
          focusNodeAdjacency: true,
          data: nodesToRender.map(node => ({
            id: node.id,
            name: node.name,
            symbolSize: node.symbolSize,
            itemStyle: { color: this.categories[node.category]?.color || '#1E90FF' },
            category: node.category,
            indicatorValue: node.indicatorValue,
            possibility: node.possibility,
            previousValue: node.previousValue
          })),
          links: linksToRender,
          categories: this.categories,
          roam: true,
          label: {
            show: true,
            color: "#333",
            fontSize: 12,
            position: 'right',
            formatter: params => params.name.length > 10 ? params.name.substring(0, 10) + '...' : params.name
          },
          lineStyle: {
            color: "source",
            curveness: 0.3,
            width: 1,
            opacity: 0.7
          },
          emphasis: {
            focus: "adjacency",
            lineStyle: { width: 3, color: "#FF4500" },
            label: { show: true }
          },
          unfocusEffect: { enabled: true, opacity: 0.2 }
        }]
      };

      this.relationChartInstance.setOption(option);
      // 渲染后强制更新图例
      this.$nextTick(() => {
        this.renderCategoryLegend();
      });
    },

    showContextMenu(event, index, isHistoryTask = false) { // 修改：添加 isHistoryTask 参数
      console.log('showContextMenu called', { index, isHistoryTask, event });
      this.contextMenuVisible = true;
      this.contextMenuX = event.clientX;
      this.contextMenuY = event.clientY;
      this.contextMenuTaskIndex = index;
      this.isHistoryTask = isHistoryTask; // 新增：记录是否为历史任务
    },

    showDeleteConfirm(event, index, isHistoryTask = false) {
      console.log('showDeleteConfirm called', { index, isHistoryTask, event });
      const task = isHistoryTask ? this.historyTasks[index] : this.activeTasks[index];
      if (!task || !task.name) {
        console.error('任务不存在:', { index, isHistoryTask });
        alert('任务不存在，请刷新页面重试');
        return;
      }
      this.contextMenuTaskIndex = index;
      this.isHistoryTask = isHistoryTask;
      this.deleteTaskName = task.name;
      this.deleteConfirmVisible = true;

      // 获取任务标签或叉号的 DOM 元素
      const selector = isHistoryTask
          ? `#historyTaskList li:nth-child(${index + 1})`
          : `#taskTabs .task-tab:nth-child(${index + 2})`; // +2 因为有“主界面查看”
      const taskElement = document.querySelector(selector);
      if (taskElement) {
        const rect = taskElement.getBoundingClientRect();
        const containerRect = document.querySelector('#container').getBoundingClientRect();
        this.deleteConfirmX = rect.right - containerRect.left;
        this.deleteConfirmY = rect.bottom - containerRect.top;
        const confirmWidth = 200;
        const confirmHeight = 80;
        if (this.deleteConfirmX + confirmWidth > containerRect.width) {
          this.deleteConfirmX = containerRect.width - confirmWidth - 10;
        }
        if (this.deleteConfirmY + confirmHeight > containerRect.height) {
          this.deleteConfirmY = rect.top - containerRect.top - confirmHeight - 10;
        }
      } else {
        console.warn('未找到任务标签元素，使用鼠标位置');
        this.deleteConfirmX = event.clientX - containerRect.left + 5;
        this.deleteConfirmY = event.clientY - containerRect.top + 5;
      }
      if (!isHistoryTask) {
        this.hideContextMenu();
      }
    },

    async deleteTask(index, isHistoryTask) {
      if (index === null || index === undefined) {
        console.error('Invalid task index:', index);
        this.cancelDeleteTask();
        return;
      }
      const task = isHistoryTask ? this.historyTasks[index] : this.activeTasks[index];
      if (!task || !task.name) {
        console.error('任务不存在:', { index, isHistoryTask });
        alert('任务不存在，请刷新页面重试');
        this.cancelDeleteTask();
        return;
      }
      console.log(`Deleting task: ${task.name}, isHistoryTask: ${isHistoryTask}`);
      alert(`任务 ${task.name} 删除中...`);
      if (!isHistoryTask && task.timerId) {
        clearInterval(task.timerId);
        task.timerId = null;
      }
      try {
        const { data } = await http.post('/relation/delTask/', {
          user: this.userId,
          task: task.name
        });
        if (data.status === 1) {
          console.log(`任务 ${task.name} 删除成功，isHistoryTask: ${isHistoryTask}`);
          alert(`任务 ${task.name} 删除成功`);
          if (isHistoryTask) {
            this.historyTasks.splice(index, 1);
            const activeIndex = this.activeTasks.findIndex(t => t.id === task.id);
            if (activeIndex !== -1) {
              if (this.activeTasks[activeIndex].timerId) {
                clearInterval(this.activeTasks[activeIndex].timerId);
              }
              this.activeTasks.splice(activeIndex, 1);
            }
          } else {
            this.activeTasks.splice(index, 1);
            const histIdx = this.historyTasks.findIndex(t => t.id === task.id);
            if (histIdx !== -1) {
              this.historyTasks.splice(histIdx, 1);
            }
          }
          if (this.currentTaskIndex === index && !isHistoryTask) {
            this.switchTask(null);
          } else if (this.currentTaskIndex !== null && !isHistoryTask) {
            this.currentTaskIndex = this.currentTaskIndex > index ? this.currentTaskIndex - 1 : this.currentTaskIndex;
          }
          this.saveHistoryTasks();
          this.$forceUpdate();
        } else {
          console.error(`任务 ${task.name} 删除失败，后端返回:`, data);
          alert(`任务 ${task.name} 删除失败，请重试`);
        }
      } catch (error) {
        console.error(`删除任务 ${task.name} 失败:`, error);
        alert(`任务 ${task.name} 删除失败，请重试`);
      }
      this.cancelDeleteTask();
    },

    async confirmDeleteTask() {
      await this.deleteTask(this.contextMenuTaskIndex, this.isHistoryTask);
    },

    // 新增：取消删除任务
    cancelDeleteTask() {
      console.log('Cancel delete task');
      this.deleteConfirmVisible = false;
      this.deleteTaskName = '';
      this.contextMenuTaskIndex = null;
      this.isHistoryTask = false;
    },

    startTaskStatusPolling() {
      if (this.taskStatusInterval) {
        return;
      }
      this.taskStatusInterval = setInterval(() => {
        this.checkAllTaskStatuses();
      }, 5000);
    },

    stopTaskStatusPolling() {
      if (this.taskStatusInterval) {
        clearInterval(this.taskStatusInterval);
        this.taskStatusInterval = null;
      }
    },

    async checkAllTaskStatuses() {
      if (!this.activeTasks.length) {
        return;
      }

      for (let i = 0; i < this.activeTasks.length; i++) {
        const task = this.activeTasks[i];
        if (!task || typeof task.progress === 'undefined') {
          continue;
        }

        if (task.progress < 100) {
          await this.checkTaskStatus(i);
        } else {
          // 如果任务已完成，则检查下一个
          continue;
        }
      }

      this.$forceUpdate();
    },


    async submitNetwork(taskName) {
      if (!this.userId) {
        console.error('用户ID为空，无法提交网络');
        alert('用户ID缺失，请登录后重试');
        return false;
      }
      if (!taskName) {
        console.warn('任务名称为空');
        alert('任务名称缺失，请重试');
        return false;
      }
      try {
        // 构建边列表
        const edges = this.links.map(link => {
          const sourceNode = this.nodes.find(node => node.id === link.source);
          const targetNode = this.nodes.find(node => node.id === link.target);
          if (!sourceNode?.originalName || !targetNode?.originalName) {
            console.warn('无效边，缺少源或目标节点:', link);
            return null;
          }
          return {
            user: this.userId,
            task: taskName,
            start_nodes: sourceNode.originalName,
            target_nodes: targetNode.originalName
          };
        }).filter(edge => edge !== null);

        if (edges.length === 0) {
          console.warn('边列表为空，无法提交网络');
          alert('网络图无有效边，请检查边设置');
          return false;
        }

        const payload = {
          edges
        };

        console.log('Submitting network with payload:', JSON.stringify(payload, null, 2));
        const response = await http.post('/relation/NewBN/', payload);

        // 兼容后端返回 1 或 { status: 1 }
        const isSuccess = response.data === 1 || (response.data && response.data.status === 1);
        if (!isSuccess) {
          throw new Error(`网络提交失败，后端返回: ${JSON.stringify(response.data)}`);
        }

        console.log(`网络 ${taskName} 提交成功`);
        this.relationModified = false; // 重置修改状态
        this.mainLinks = JSON.parse(JSON.stringify(this.links)); // 同步 mainLinks
        return true;
      } catch (error) {
        let errorMessage = '网络提交失败，请检查网络或联系管理员';
        if (error.response) {
          errorMessage = `网络提交失败，状态码：${error.response.status}，详情: ${error.response.data?.error || error.response.data?.message || error.message}`;
          if (error.response.status === 404) {
            errorMessage = '接口 /relation/NewBN/ 不存在，请检查后端服务或接口地址';
          } else if (error.response.status === 400) {
            errorMessage = `请求参数错误：${error.response.data?.error || error.response.data?.message || '请检查提交的数据格式'}`;
          } else if (error.response.status === 500) {
            errorMessage = `服务器错误：${error.response.data?.error || error.response.data?.message || '请联系管理员'}`;
          }
        } else if (error.request) {
          errorMessage = '无法连接到服务器，请检查网络或后端服务状态';
        }
        console.error('网络提交失败:', {
          message: error.message,
          response: error.response ? {
            status: error.response.status,
            data: error.response.data
          } : null,
          request: error.request
        });
        alert(errorMessage);
        return false;
      }
    },

    async confirmAddEdge() {
      if (!this.addEdge.from || !this.addEdge.to) {
        alert('请选择起点和终点');
        return;
      }
      if (this.addEdge.from === this.addEdge.to) {
        alert("起点和终点不能相同");
        return;
      }
      const exists = this.links.some(link => link.source === this.addEdge.from && link.target === this.addEdge.to);
      if (exists) {
        alert("该边已存在");
        return;
      }

      // 临时添加边
      const newLink = { source: this.addEdge.from, target: this.addEdge.to, rate: Math.random() - 0.5 };
      this.links.push(newLink);

      // 检查环路
      if (this.hasCycle()) {
        console.warn('添加边后形成环路，回滚:', newLink);
        this.links.pop();
        alert('添加该边会导致环路，请调整');
        return;
      }

      // 标记修改
      this.relationModified = true;
      console.log('边添加成功:', newLink);

      // 清空选择
      this.addEdge.from = "";
      this.addEdge.to = "";
      this.$nextTick(() => this.renderRelationChart());
    },

    async confirmRemoveEdge() {
      if (!this.removeEdge.from || !this.removeEdge.to) {
        alert('请选择要删除的边');
        return;
      }
      const index = this.links.findIndex(link => link.source === this.removeEdge.from && link.target === this.removeEdge.to);
      if (index === -1) {
        alert("该边不存在");
        return;
      }

      // 删除边
      const removedLink = this.links.splice(index, 1)[0];
      this.relationModified = true;
      console.log('边删除成功:', removedLink);

      // 清空选择
      this.removeEdge.from = "";
      this.removeEdge.to = "";
      this.updateRemoveOptions();
      this.$nextTick(() => this.renderRelationChart());
    },

    async initializeData() {
      try {
        await Promise.all([
          this.fetchRelationData(),
          this.fetchHistoryTasks()
        ]);
        await this.$nextTick();
        this.renderRelationChart();
      } catch (error) {
        console.error('初始化数据失败:', error);
        alert('初始化数据失败，请刷新页面或联系支持');
        this.renderRelationChart();
      }
    },

    setupEventListeners() {
      window.addEventListener("click", this.hideContextMenu);
    },
    cleanupEventListeners() {
      window.removeEventListener("click", this.hideContextMenu);
    },
    saveHistoryTasks() {
      localStorage.setItem("historyTasks", JSON.stringify(this.historyTasks));
    },

    async fetchRelationData() {
      try {
        const { data } = await http.get('/relation/BN/', { params: { user: this.userId } });
        const { edges, data: legend_data } = data;

        // 处理关系图数据
        if (!Array.isArray(edges)) {
          console.warn('指标因果网络边数据无效，初始化为空:', edges);
          this.nodes = [];
          this.links = [];
          this.mainNodes = [];
          this.mainLinks = [];
          this.validNodeNames = new Set();
          this.legendNodes = [];
          this.legendData = {}; // 初始化为空
          this.relationModified = false;
          return;
        }

        // 存储 legend_data
        this.legendData = legend_data || {};

        this.validNodeNames = new Set();
        edges.forEach(([source, target]) => {
          if (source) this.validNodeNames.add(this.normalizeNodeName(source));
          if (target) this.validNodeNames.add(this.normalizeNodeName(target));
        });

        // 初始化节点
        this.nodes = Array.from(this.validNodeNames).map((nodeName, index) => {
          const chineseName = NAME_MAPPING[nodeName] || nodeName;
          const originalValue = legend_data && legend_data[chineseName] ? parseFloat(legend_data[chineseName]) : 0;
          return {
            id: String(index + 1),
            name: chineseName,
            originalName: nodeName,
            symbolSize: 50,
            category: this.determineCategory(nodeName),
            possibility: 0,
            indicatorValue: originalValue,
            previousValue: 0,
            originalValue,
            userModified: false,
            isEvidence: false,
            propagationLevel: 0
          };
        });

        this.mainNodes = JSON.parse(JSON.stringify(this.nodes));

        const nameToId = Object.fromEntries(this.nodes.map(node => [node.originalName, node.id]));
        this.links = edges
            .filter(([source, target]) => source !== null && source !== undefined)
            .map(([source, target], index) => {
              const sourceName = this.normalizeNodeName(source);
              const targetName = target ? this.normalizeNodeName(target) : null;
              const sourceId = nameToId[sourceName];
              if (!sourceId) {
                console.warn(`未找到源节点 at index ${index}: ${sourceName}`);
                return null;
              }
              if (targetName) {
                const targetId = nameToId[targetName];
                if (targetId) {
                  return {
                    source: sourceId,
                    target: targetId,
                    rate: 0
                  };
                } else {
                  console.warn(`未找到目标节点 at index ${index}: ${targetName}`);
                }
              }
              return null;
            })
            .filter(link => link !== null);

        this.mainLinks = JSON.parse(JSON.stringify(this.links));

        this.legendNodes = JSON.parse(JSON.stringify(this.mainNodes));

        this.relationModified = false;
        console.log('Initialized legendNodes:', this.legendNodes);
        console.log('Stored legendData:', this.legendData);
      } catch (error) {
        console.error('获取指标因果网络和图例数据失败:', error);
        this.nodes = [];
        this.links = [];
        this.mainNodes = [];
        this.mainLinks = [];
        this.validNodeNames = new Set();
        this.legendNodes = [];
        this.legendData = {};
        this.relationModified = false;
      }
    },

    async fetchHistoryTasks() {
      try {
        const { data } = await http.get('/relation/historytask/', { params: { user: this.userId } });
        if (!data || !Array.isArray(data.data)) {
          throw new Error('历史任务数据格式错误：data.data 不是数组');
        }

        this.historyTasks = data.data.map(task => {
          if (!task.task || !task.date) {
            console.warn('无效的任务对象，缺少 task 或 date 字段:', task);
            return null;
          }
          return {
            id: task.task_id || uuidv4(),
            name: task.task,
            created_at: new Date(task.date),
            user: task.user,
            progress: 0,
            flash: false,
            timerId: null
          };
        }).filter(task => task);

        this.taskCounter = this.historyTasks.reduce((max, task) => {
          const match = task.name.match(/^任务(\d+)$/);
          if (match) {
            const num = parseInt(match[1], 10);
            return Math.max(max, num);
          }
          return max;
        }, 0);

        this.historyTasks.sort((a, b) => b.created_at - a.created_at);

        const today = new Date();
        today.setHours(0, 0, 0, 0);
        const activeTasks = this.historyTasks.filter(task => {
          const taskDate = new Date(task.created_at);
          taskDate.setHours(0, 0, 0, 0);
          return taskDate.getTime() === today.getTime();
        });

        this.$set(this, 'activeTasks', []);
        activeTasks.forEach((task, index) => {
          this.$set(this.activeTasks, index, { ...task });
        });

        this.historyTasks = this.historyTasks.filter(task => {
          const taskDate = new Date(task.created_at);
          taskDate.setHours(0, 0, 0, 0);
          return taskDate.getTime() < today.getTime();
        });

        for (let i = 0; i < this.activeTasks.length; i++) {
          await this.checkTaskStatus(i);
        }

        this.saveHistoryTasks();
      } catch (error) {
        console.error('获取历史任务失败:', error);
        this.$set(this, 'activeTasks', []);
        this.historyTasks = [];
        this.saveHistoryTasks();
      }
    },

    async checkTaskStatus(index) {
      if (index < 0 || index >= this.activeTasks.length) {
        console.error(`无效的任务索引: ${index}, activeTasks 长度: ${this.activeTasks.length}`);
        return;
      }

      const task = this.activeTasks[index];
      if (!task || !task.name) {
        console.error(`索引 ${index} 处的任务无效:`, task);
        return;
      }

      try {
        console.log(`检查任务状态: ${task.name}, user_id: ${this.userId}, 索引: ${index}`);
        const response = await http.get('/relation/taskstatus/', {
          params: { user: this.userId, task: task.name }
        });

        const { data } = response;
        if (!data || typeof data !== 'object') {
          throw new Error('响应数据无效');
        }
        if (!data.data || typeof data.data !== 'object') {
          throw new Error('响应中缺少 data.data 对象');
        }

        const { task: taskName, status, total_status } = data.data;
        if (taskName === undefined || status === undefined || total_status === undefined) {
          throw new Error(`响应缺少必要字段: task=${taskName}, status=${status}, total_status=${total_status}`);
        }

        const numStatus = parseInt(status, 10);
        const numTotalStatus = parseInt(total_status, 10);
        console.log(`转换后: status=${numStatus}, total_status=${numTotalStatus}`);

        const progress = numTotalStatus > 0 && !isNaN(numStatus) && !isNaN(numTotalStatus)
            ? Math.round((numStatus / numTotalStatus) * 100)
            : 0;

        // 使用 $set 确保反应性
        this.$set(this.activeTasks[index], 'progress', progress);
        console.log(`任务 ${task.name} 进度更新为: ${progress}%`);

        if (progress === 100 && !task.flash) {
          this.$set(this.activeTasks[index], 'flash', true);
          alert(`任务 ${task.name} 生成完成`);
          if (task.timerId) {
            clearInterval(task.timerId);
            this.$set(this.activeTasks[index], 'timerId', null);
          }
          // 自动尝试切换到任务界面
          await this.switchTask(index);
        }

        this.saveHistoryTasks();
        this.$nextTick(() => {
          this.$forceUpdate();
        });
      } catch (error) {
        console.error(`任务 ${task.name} 状态查询失败:`, error);
        alert(`任务 ${task.name} 状态查询失败：${error.message}，请检查网络或稍后重试`);
      }
    },

    async fetchPredictionData(taskName) {
      try {
        const { data } = await http.get('/relation/getPre/', {
          params: { user: this.userId, task: taskName }
        });
        console.log(`fetchPredictionData for ${taskName}:`, data);
        return data || {}; // 确保返回对象
      } catch (error) {
        console.error(`获取预测数据失败 for task ${taskName}:`, error);
        return {};
      }
    },

    async fetchEvidenceData(taskName) {
      if (!taskName || typeof taskName !== 'string') {
        console.error('无效的任务名称:', taskName);
        return [];
      }
      try {
        const { data } = await http.get('/relation/getEV/', {
          params: { user: this.userId, task: taskName }
        });
        console.log(`fetchEvidenceData 返回数据 for ${taskName}:`, data);
        // 验证返回数据结构
        if (!data || typeof data !== 'object') {
          console.warn(`证据数据格式错误：data 不是对象，实际返回: ${JSON.stringify(data)}`);
          return [];
        }
        if (!Array.isArray(data.data)) {
          console.warn(`数据类型错误：data.data 不是数组，实际返回: ${JSON.stringify(data)}`);
          return [];
        }
        // 验证并清理数据
        const cleanedData = data.data.filter((item, index) => {
          if (!item || typeof item !== 'object' || typeof item.stage !== 'number' || !Array.isArray(item.variables)) {
            console.warn(`stage ${index + 1} 的结构无效，跳过: ${JSON.stringify(item)}`);
            return false;
          }
          item.variables = item.variables.filter((variable, varIndex) => {
            if (!variable || typeof variable.name !== 'string' || typeof variable.value !== 'number' || !isFinite(variable.value)) {
              console.warn(`stage ${index + 1} 的变量 ${varIndex + 1} 无效，跳过: ${JSON.stringify(variable)}`);
              return false;
            }
            return true;
          });
          return item.variables.length > 0; // 仅保留有有效变量的 stage
        });
        console.log(`清理后的 evidenceData for ${taskName}:`, cleanedData);
        return cleanedData;
      } catch (error) {
        console.error(`获取证据数据失败 for task ${taskName}:`, error);
        return [];
      }
    },

    resetNetwork() {
      this.nodes = JSON.parse(JSON.stringify(this.mainNodes));
      this.links = JSON.parse(JSON.stringify(this.mainLinks));
      this.relationModified = false;
      this.$nextTick(() => this.renderRelationChart());
    },

    async fetchTaskNetwork(taskName) {
      try {
        const { data } = await http.get('/relation/BN2/', {
          params: { user: this.userId, task: taskName }
        });
        return data;
      } catch (error) {
        console.error(`获取任务网络数据失败 for task ${taskName}:`, error);
        return null;
      }
    },

    formatTaskTime(date) {
      const d = new Date(date);
      return `${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
    },

    formatHistoryTaskDate(date) {
      const d = new Date(date);
      return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
    },

    normalizeNodeName(name) {
      if (typeof name !== 'string') {
        console.warn('Node name is not a string:', name);
        return String(name);
      }
      if (!NAME_MAPPING[name] && REVERSE_NAME_MAPPING[name]) {
        return REVERSE_NAME_MAPPING[name];
      }
      return name;
    },

    determineCategory(nodeName) {
      if (typeof nodeName !== 'string') {
        console.warn('Invalid nodeName for category determination:', nodeName);
        return 0;
      }
      if (nodeName.startsWith('us_') || nodeName.includes('libor')) {
        return 1;
      }
      if (['M2_GR', 'ch_GDP_GR', 'LPR1Y_GR', 'shbor_GR'].includes(nodeName)) {
        return 2;
      }
      return 0;
    },

    hasCycle() {
      const graph = {};
      this.nodes.forEach(node => {
        graph[node.id] = [];
      });
      this.links.forEach(link => {
        graph[link.source].push(link.target);
      });

      const visited = new Set();
      const recStack = new Set();

      const dfs = (node) => {
        if (!visited.has(node)) {
          visited.add(node);
          recStack.add(node);

          for (const neighbor of graph[node]) {
            if (!visited.has(neighbor) && dfs(neighbor)) {
              return true;
            } else if (recStack.has(neighbor)) {
              return true;
            }
          }
        }
        recStack.delete(node);
        return false;
      };

      for (const node in graph) {
        if (!visited.has(node) && dfs(node)) {
          return true;
        }
      }
      return false;
    },

    handleValueChange(node, event) {
      const newVal = parseFloat(event.target.value);
      if (isNaN(newVal) || newVal === node.indicatorValue) return;

      node.previousValue = node.originalValue;
      node.indicatorValue = newVal;
      node.possibility = node.previousValue !== 0 ? ((newVal - node.previousValue) / Math.abs(node.previousValue)) * 100 : 0;
      node.userModified = true;
      node.propagationLevel = 0;

      const mainNode = this.mainNodes.find(n => n.id === node.id);
      if (mainNode) {
        Object.assign(mainNode, {
          indicatorValue: newVal,
          previousValue: node.originalValue,
          possibility: node.possibility,
          userModified: true,
          propagationLevel: 0
        });
      }
    },

    adjustValue(node, direction) {
      if (!this.relationLocked) return;

      let change = node.originalValue * 0.01 * direction;
      if (node.originalValue === 0) {
        change = 0.01 * direction;
      }

      const newVal = parseFloat((node.indicatorValue + change).toFixed(2));
      if (newVal === node.indicatorValue) return;

      node.previousValue = node.originalValue;
      node.indicatorValue = newVal;
      node.possibility = node.previousValue !== 0 ? ((newVal - node.previousValue) / Math.abs(node.previousValue)) * 100 : 0;
      node.userModified = true;
      node.propagationLevel = 0;

      const mainNode = this.mainNodes.find(n => n.id === node.id);
      if (mainNode) {
        Object.assign(mainNode, {
          indicatorValue: newVal,
          previousValue: node.originalValue,
          possibility: node.possibility,
          userModified: true,
          propagationLevel: 0
        });
      }

      this.$nextTick(() => {
        const input = document.querySelector(`#topLegend .legend-item input[value="${node.indicatorValue.toFixed(2)}"]`);
        if (input) {
          input.value = newVal.toFixed(2);
        }
      });
    },

    getNodeTextColor(node) {
      if (this.taskViewMode) {
        if (node.isEvidence || node.userModified) return 'red-text';
        if (node.propagationLevel === parseInt(this.selectedOrder) && node.propagationLevel > 0) return 'green-text';
      } else {
        if (node.userModified) return 'red-text';
      }
      return '';
    },

    // renderRelationChart() {
    //   const container = document.getElementById("relationChart");
    //   if (!container) {
    //     console.error('未找到指标因果网络容器 #relationChart');
    //     return;
    //   }
    //
    //   if (this.relationChartInstance) {
    //     this.relationChartInstance.dispose();
    //   }
    //   this.relationChartInstance = echarts.init(container);
    //
    //   const nodesToRender = this.nodes.length > 0 ? this.nodes : [{
    //     id: "placeholder",
    //     name: "无数据",
    //     symbolSize: 50,
    //     category: 0,
    //     indicatorValue: 0,
    //     possibility: 0,
    //     previousValue: 0
    //   }];
    //
    //   const linksToRender = this.links.length > 0 ? this.links : [];
    //
    //   const option = {
    //     tooltip: {
    //       formatter: params => `
    //         <div style="padding:5px;">
    //           <strong>${params.data.name}</strong><br/>
    //           当前值: ${parseFloat(params.data.indicatorValue).toFixed(2)}<br/>
    //           变化率: ${params.data.possibility >= 0 ? '+' : ''}${parseFloat(params.data.possibility).toFixed(2)}%<br/>
    //           变化: ${parseFloat(params.data.previousValue).toFixed(2)} → ${parseFloat(params.data.indicatorValue).toFixed(2)}
    //         </div>`,
    //       extraCssText: "text-align: left;"
    //     },
    //     series: [{
    //       type: "graph",
    //       layout: "force",
    //       edgeSymbol: ['none', 'arrow'],
    //       edgeSymbolSize: [4, 10],
    //       force: {
    //         repulsion: 500,
    //         edgeLength: [50, 150],
    //         gravity: 0.05
    //       },
    //       focusNodeAdjacency: true,
    //       data: nodesToRender.map(node => ({
    //         id: node.id,
    //         name: node.name,
    //         symbolSize: node.symbolSize,
    //         itemStyle: { color: this.categories[node.category].color },
    //         category: node.category,
    //         indicatorValue: node.indicatorValue,
    //         possibility: node.possibility,
    //         previousValue: node.previousValue
    //       })),
    //       links: linksToRender,
    //       categories: this.categories,
    //       roam: true,
    //       label: {
    //         show: true,
    //         color: "#333",
    //         fontSize: 12,
    //         position: 'right',
    //         formatter: params => params.name.length > 10 ? params.name.substring(0, 10) + '...' : params.name
    //       },
    //       lineStyle: {
    //         color: "source",
    //         curveness: 0.3,
    //         width: 1,
    //         opacity: 0.7
    //       },
    //       emphasis: {
    //         focus: "adjacency",
    //         lineStyle: { width: 3, color: "#FF4500" },
    //         label: { show: true }
    //       },
    //       unfocusEffect: { enabled: true, opacity: 0.2 }
    //     }]
    //   };
    //
    //   this.relationChartInstance.setOption(option);
    // },
    //
    // resizeChart() {
    //   if (this.relationChartInstance) {
    //     this.relationChartInstance.resize();
    //   }
    // },

    lockRelationChart() {
      this.relationLocked = true;
      this.lockedRelationNodes = JSON.parse(JSON.stringify(this.nodes));
      alert("指标因果网络已锁定，不可再次修改。");
    },

    unlockRelationChart() {
      this.relationLocked = false;
      this.$nextTick(() => this.renderRelationChart());
    },

    async generateTask() {
      if (!this.areAllTasksComplete) {
        alert('请等待当前任务完成后再次生成新任务');
        return;
      }
      if (!this.relationLocked) {
        alert('请先锁定网络后再生成任务');
        return;
      }
      if (this.hasCycle()) {
        alert('当前网络图中存在环路，无法生成任务，请检查边设置');
        return;
      }

      const evidenceNodes = this.legendNodes
          .filter(node => node.userModified)
          .map(node => ({
            variable: node.originalName,
            old_value: node.previousValue,
            new_value: node.indicatorValue
          }));

      if (evidenceNodes.length === 0) {
        alert('请至少修改一个指标值作为证据');
        return;
      }

      try {
        const taskId = uuidv4();
        const taskName = `任务${++this.taskCounter}`;
        const task = {
          id: taskId,
          name: taskName,
          created_at: new Date(),
          user: this.userId,
          progress: 0,
          flash: false,
          timerId: null,
          nodes: JSON.parse(JSON.stringify(this.nodes)),
          links: JSON.parse(JSON.stringify(this.links)),
          evidenceNodes,
          initialLegendNodes: JSON.parse(JSON.stringify(this.legendNodes)),
          lockedRelationNodes: JSON.parse(JSON.stringify(this.nodes)),
          evidenceData: [],
          predictionData: {},
          slcData: {}
        };

        // 提交网络（如果修改过）
        if (this.relationModified) {
          const networkSuccess = await this.submitNetwork(taskName);
          if (!networkSuccess) {
            alert('网络提交失败，无法生成任务');
            return;
          }
        } else {
          console.log('网络未修改，跳过 /relation/NewBN/ 调用');
        }

        // 提交任务
        this.activeTasks.push(task);
        this.historyTasks.unshift({ ...task, created_at: new Date(task.created_at) });
        this.saveHistoryTasks();

        const payload = {
          user: this.userId,
          task: taskName,
          evidence: evidenceNodes.map(node => ({
            variable: node.variable,
            old_value: node.old_value,
            new_value: node.new_value
          })),
          forecast_month: this.forecastMonth
        };

        console.log('Submitting task with payload:', JSON.stringify(payload, null, 2));
        const response = await http.post('/relation/NewTask/', payload);

        // 兼容后端返回 1 或 { status: 1 }
        const isSuccess = response.data === 1 || (response.data && response.data.status === 1);
        if (!isSuccess) {
          throw new Error(`任务提交失败，后端返回: ${JSON.stringify(response.data)}`);
        }

        alert(`任务 ${taskName} 已提交，正在处理中...`);
        // 重置图例数据
        this.$set(this, 'legendNodes', JSON.parse(JSON.stringify(this.mainNodes)).map(node => ({
          ...node,
          indicatorValue: node.originalValue || 0,
          previousValue: 0,
          possibility: 0,
          userModified: false,
          isEvidence: false,
          propagationLevel: 0
        })));
        this.relationModified = false;
        this.$forceUpdate();
        console.log('legendNodes after reset:', this.legendNodes);

        await this.checkTaskStatus(this.activeTasks.length - 1);
      } catch (error) {
        let errorMessage = '任务生成失败，请检查网络或联系管理员';
        if (error.response) {
          errorMessage = `任务生成失败，状态码：${error.response.status}，详情：${error.response.data?.message || error.message}`;
          if (error.response.status === 404) {
            errorMessage = '接口 /relation/NewTask/ 不存在，请检查后端服务或接口地址';
          } else if (error.response.status === 400) {
            errorMessage = `请求参数错误：${error.response.data?.message || '请检查提交的数据格式'}`;
          }
        } else if (error.request) {
          errorMessage = '无法连接到服务器，请检查网络或后端服务状态';
        }
        console.error('任务生成失败:', {
          message: error.message,
          response: error.response ? {
            status: error.response.status,
            data: error.response.data
          } : null,
          request: error.request
        });
        alert(errorMessage);
      }
    },

    // prepareLegendData() {
    //   return this.legendNodes
    //       .filter(node => {
    //         const isValid = this.validNodeNames.has(node.originalName);
    //         return node.userModified && isValid;
    //       })
    //       .map(node => {
    //         return {
    //           variable: node.originalName,
    //           old_value: node.originalValue,
    //           new_value: node.indicatorValue
    //         };
    //       });
    // },

    updateRemoveOptions() {
      if (!this.removeEdge.from) {
        this.removeOptions = [];
        this.removeEdge.to = "";
        return;
      }
      const targets = this.links.filter(link => link.source === this.removeEdge.from).map(link => link.target);
      this.removeOptions = this.nodes.filter(node => targets.includes(node.id));
      if (!this.removeOptions.some(node => node.id === this.removeEdge.to)) {
        this.removeEdge.to = "";
      }
    },

    // confirmAddEdge() {
    //   if (!this.addEdge.from || !this.addEdge.to) return;
    //   if (this.addEdge.from === this.addEdge.to) {
    //     alert("起点和终点不能相同");
    //     return;
    //   }
    //   const exists = this.links.some(link => link.source === this.addEdge.from && link.target === this.addEdge.to);
    //   if (exists) {
    //     alert("该边已存在");
    //     return;
    //   }
    //   this.links.push({ source: this.addEdge.from, target: this.addEdge.to, rate: Math.random() - 0.5 });
    //   this.mainLinks = JSON.parse(JSON.stringify(this.links)); // 同步到 mainLinks
    //   this.addEdge.from = "";
    //   this.addEdge.to = "";
    //   this.$nextTick(() => this.renderRelationChart());
    // },

    // confirmRemoveEdge() {
    //   if (!this.removeEdge.from || !this.removeEdge.to) return;
    //   const index = this.links.findIndex(link => link.source === this.removeEdge.from && link.target === this.removeEdge.to);
    //   if (index === -1) {
    //     alert("该边不存在");
    //     return;
    //   }
    //   this.links.splice(index, 1);
    //   this.mainLinks = JSON.parse(JSON.stringify(this.links)); // 同步到 mainLinks
    //   this.removeEdge.from = "";
    //   this.removeEdge.to = "";
    //   this.updateRemoveOptions();
    //   this.$nextTick(() => this.renderRelationChart());
    // },


    async switchTask(taskIndex) {
      console.log(`switchTask called with taskIndex: ${taskIndex}`);
      if (taskIndex !== null) {
        const task = this.activeTasks[taskIndex];
        if (!task || !task.name) {
          console.error(`无效的任务对象 at index ${taskIndex}:`, task);
          alert('无法切换任务：任务数据无效，请返回主界面');
          this.switchTask(null);
          return;
        }

        if (task.progress < 100) {
          alert(`任务 ${task.name} 未完成（进度 ${task.progress}%），无法进入`);
          return;
        }

        // 移除高亮
        if (task.flash) {
          this.$set(this.activeTasks[taskIndex], 'flash', false);
        }


        console.log(`切换任务: ${task.name}, 初始数据状态:`, {
          evidenceData: task.evidenceData,
          initialLegendNodes: task.initialLegendNodes,
          lockedRelationNodes: task.lockedRelationNodes,
          nodes: task.nodes,
          links: task.links
        });

        try {
          // 确保所有方法存在
          const requiredMethods = ['fetchTaskNetwork', 'fetchEvidenceData', 'fetchPredictionData', 'fetchSLCData'];
          for (const method of requiredMethods) {
            if (typeof this[method] !== 'function') {
              throw new Error(`${method} 方法未定义`);
            }
          }

          // 同时调用四个接口
          console.log(`Fetching data for task: ${task.name}`);
          const [networkData, evidenceData, predictionData, slcData] = await Promise.all([
            this.fetchTaskNetwork(task.name).catch(error => {
              console.warn(`fetchTaskNetwork 失败，跳过处理: ${error.message}`);
              return { edges: [] }; // Fallback to empty edges
            }),
            this.fetchEvidenceData(task.name).catch(error => {
              console.warn(`fetchEvidenceData 失败，跳过处理: ${error.message}`);
              return task.evidenceData || [];
            }),
            this.fetchPredictionData(task.name).catch(error => {
              console.warn(`fetchPredictionData 失败，跳过处理: ${error.message}`);
              return task.predictionData || {};
            }),
            this.fetchSLCData(task.name).catch(error => {
              console.warn(`fetchSLCData 失败，跳过处理: ${error.message}`);
              return task.slcData || {};
            })
          ]);


          // 初始化网络图节点
          if (!networkData || typeof networkData !== 'object' || !Array.isArray(networkData.edges)) {
            console.warn('网络图数据无效，使用空边数据:', networkData);
            networkData = { edges: [] };
            }
          const nodeSet = new Set();
          networkData.edges.forEach(([source, target]) => {
            nodeSet.add(source);
            nodeSet.add(target);
            });
          const nodes = Array.from(nodeSet).map((originalName, index) => {
            const chineseName = NAME_MAPPING[originalName] || originalName;
            const originalValue = this.legendData[chineseName] ? parseFloat(this.legendData[chineseName]) : 0;
            return {
              id: String(index + 1),
              name: chineseName,
              originalName,
              originalValue,
              indicatorValue: originalValue,
              previousValue: 0,
              possibility: 0,
              userModified: false,
              isEvidence: false,
              propagationLevel: 0,
              symbolSize: 50,
              category: this.determineCategory(originalName)
            };
          });
          const links = networkData.edges.map(([source, target], index) => ({
              id: String(index + 1),
              source: nodes.find(n => n.originalName === source)?.id,
              target: nodes.find(n => n.originalName === target)?.id,
              rate: 0
          })).filter(link => link.source && link.target);

          if (!Array.isArray(evidenceData)) {
            console.warn(`证据数据不是数组，实际返回: ${JSON.stringify(evidenceData)}，使用默认空数组`);
            evidenceData = [];
          }
          if (!task.initialLegendNodes || !Array.isArray(task.initialLegendNodes)) {
            console.warn('initialLegendNodes 无效，初始化为 /relation/BN2/ 节点');
            this.$set(task, 'initialLegendNodes', nodes);
            }
          if (!task.lockedRelationNodes || !Array.isArray(task.lockedRelationNodes)) {
            console.warn('lockedRelationNodes 无效，尝试从 nodes 初始化');
            this.$set(task, 'lockedRelationNodes', nodes);
          }

          this.$set(task, 'nodes', nodes);
          this.$set(task, 'links', links);
          this.$set(task, 'evidenceData', evidenceData);
          this.$set(task, 'predictionData', predictionData || {});
          this.$set(task, 'slcData', slcData || {});
          console.log(`更新后的任务数据:`, {
            nodes: task.nodes,
            links: task.links,
            evidenceData: task.evidenceData,
            initialLegendNodes: task.initialLegendNodes,
            lockedRelationNodes: task.lockedRelationNodes,
            predictionData: task.predictionData
          });

          // 更新全局状态
          this.$set(this, 'legendNodes', JSON.parse(JSON.stringify(task.initialLegendNodes)));
          this.$set(this, 'nodes', task.nodes);
          this.$set(this, 'links', task.links);
          this.$set(this, 'lockedRelationNodes', task.lockedRelationNodes);
          this.$set(this, 'taskViewMode', true);
          this.$set(this, 'currentTaskIndex', taskIndex);

          // // 保存任务状态
          // this.$set(this.activeTasks, taskIndex, task);
          // this.saveHistoryTasks();

          // 延迟渲染，确保 DOM 就绪
          await this.$nextTick();
          setTimeout(() => {
            console.log('Rendering task interface');
            this.renderLockedRelationChart();
            this.$set(this, 'selectedOrder', 0); // 重置阶数为0
            this.updateDataByOrder();
            this.renderLineCharts(task.predictionData);
          }, 100);
        } catch (error) {
          console.error('切换任务失败:', error);
          alert(`切换任务失败，错误详情：${error.message}，请返回主界面或联系支持`);
          this.switchTask(null);
          return;
        }
      } else {
        console.log('Switching to main interface');
        this.$set(this, 'taskViewMode', false);
        this.$set(this, 'currentTaskIndex', null);
        this.$set(this, 'nodes', JSON.parse(JSON.stringify(this.mainNodes)));
        this.$set(this, 'links', JSON.parse(JSON.stringify(this.mainLinks)));
        // 修改：重置 legendNodes 到初始状态，不保留任务中的证据
        this.$set(this, 'legendNodes', JSON.parse(JSON.stringify(this.mainNodes)).map(node => ({
          ...node,
          indicatorValue: node.originalValue || 0,
          previousValue: 0,
          possibility: 0,
          userModified: false,
          isEvidence: false,
          propagationLevel: 0
        })));
        await this.$nextTick();
        this.renderRelationChart();
      }
    },

    // 新增：重置证据方法
    resetEvidence() {
      if (!this.relationLocked) {
        alert('请先锁定网络后再重置证据');
        return;
      }
      this.$set(this, 'legendNodes', JSON.parse(JSON.stringify(this.mainNodes)).map(node => ({
        ...node,
        indicatorValue: node.originalValue || 0,
        previousValue: 0,
        possibility: 0,
        userModified: false,
        isEvidence: false,
        propagationLevel: 0
      })));
      console.log('证据已重置，legendNodes:', this.legendNodes);
      this.$forceUpdate();
    },

    async fetchSLCData(taskName) {
      try {
        const { data } = await http.get('/relation/getSLC/', {
          params: { user: this.userId, task: taskName }
        });
        console.log(`fetchSLCData for ${taskName}:`, data);
        if (!data || typeof data !== 'object') {
          throw new Error(`SLC数据格式错误：data 不是对象，实际返回: ${JSON.stringify(data)}`);
        }
        return data.future_df_1 || {};
      } catch (error) {
        console.error(`获取SLC数据失败 for task ${taskName}:`, error);
        return {};
      }
    },

    processEvidenceData(data, stage) {
      console.log(`Processing evidenceData for stage ${stage}:`, data);

      if (!Array.isArray(data) || data.length === 0) {
        console.warn(`evidenceData 为空或无效 for stage ${stage}`);
        return [];
      }

      const stageData = data.find(item => item.stage === stage)?.variables || [];
      if (!Array.isArray(stageData)) {
        console.warn(`stage ${stage} 的 variables 不是数组:`, stageData);
        return [];
      }

      const result = stageData
          .filter(variable => {
            if (!variable || typeof variable !== 'object' || !variable.name || typeof variable.value !== 'number') {
              console.warn(`无效的变量数据:`, variable);
              return false;
            }
            const mappedName = REVERSE_NAME_MAPPING[variable.name] || variable.name;
            const isValid = this.validNodeNames.has(mappedName);
            if (!isValid) {
              console.warn(`变量 ${mappedName} 未在 validNodeNames 中，跳过`);
            }
            return isValid;
          })
          .map((variable, index) => {
            const mappedName = REVERSE_NAME_MAPPING[variable.name] || variable.name;
            const chineseName = NAME_MAPPING[mappedName] || mappedName;
            const indicatorValue = parseFloat(variable.value) || 0;
            const previousValue = this.legendData[chineseName] ? parseFloat(this.legendData[chineseName]) : 0;
            const possibility = previousValue !== 0 ? ((indicatorValue - previousValue) / Math.abs(previousValue)) * 100 : 0;
            return {
              id: String(index + 1),
              name: chineseName,
              originalName: mappedName,
              symbolSize: 50,
              category: this.determineCategory(mappedName),
              possibility,
              indicatorValue,
              previousValue,
              originalValue: previousValue,
              userModified: stage === 0,
              isEvidence: stage === 0,
              propagationLevel: stage > 0 ? stage : 0
            };
          });

      console.log(`Processed evidenceData for stage ${stage}:`, result);
      return result;
    },

    historyTaskClick(taskId) {
      const task = this.historyTasks.find(task => task.id === taskId);
      if (task) {
        const index = this.activeTasks.findIndex(t => t.id === task.id);
        if (index === -1) {
          // 将历史任务添加到 activeTasks 并切换
          const newTask = { ...task, progress: 100 }; // 假设历史任务已完成
          this.activeTasks.push(newTask);
          const newIndex = this.activeTasks.length - 1;
          this.$set(this, 'currentTaskIndex', newIndex);
          this.$set(this, 'taskViewMode', true);
          this.$nextTick(() => {
            this.switchTask(newIndex);
          });
        } else {
          this.switchTask(index);
        }
      }
    },

    async handleContextMenuAction(action) {
      if (action === "delete") {
        this.showDeleteConfirm();
      }
    },

    hideContextMenu() {
      console.log('Hiding context menu');
      this.contextMenuVisible = false;
    },

    // removeFlash(task) {
    //   if (task.flash) {
    //     task.flash = false;
    //   }
    // },
    renderLockedRelationChart() {
      const container = document.getElementById("lockedRelationChart");
      if (!container) {
        console.error('未找到锁定指标因果网络容器 #lockedRelationChart');
        return;
      }

      // 验证 categories 数据
      if (!this.categories.every(cat => cat.color && typeof cat.color === 'string')) {
        console.warn('Invalid categories detected, resetting to default');
        this.$set(this, 'categories', DEFAULT_CATEGORIES);
      }

      // 验证数据有效性
      let nodesToRender = Array.isArray(this.lockedRelationNodes) ? [...this.lockedRelationNodes] : [];
      if (nodesToRender.length === 0) {
        console.warn('lockedRelationNodes 为空，尝试从任务数据恢复');
        const task = this.activeTasks[this.currentTaskIndex];
        nodesToRender = task && Array.isArray(task.lockedRelationNodes) ? [...task.lockedRelationNodes] : [{
          id: 'placeholder',
          name: '无数据',
          symbolSize: 50,
          category: 0,
          indicatorValue: 0,
          possibility: 0,
          previousValue: 0
        }];
      }

      let linksToRender = Array.isArray(this.links) ? [...this.links] : [];
      if (linksToRender.length === 0) {
        console.warn('links 为空，尝试从任务数据恢复');
        const task = this.activeTasks[this.currentTaskIndex];
        linksToRender = task && Array.isArray(task.links) ? [...task.links] : [];
      }

      // 确保节点和边的 ID 有效
      nodesToRender = nodesToRender.map(node => ({
          ...node,
          id: node.id || `node-${Math.random()}`,
          name: node.name || '未知节点'
      }));
      linksToRender = linksToRender.filter(link => {
        const sourceExists = nodesToRender.some(node => node.id === link.source);
        const targetExists = nodesToRender.some(node => node.id === link.target);
        if (!sourceExists || !targetExists) {
          console.warn(`无效边: source=${link.source}, target=${link.target}`);
          return false;
          }
        return true;
        });

      let lockedChart = echarts.getInstanceByDom(container);
      if (!lockedChart) {
        lockedChart = echarts.init(container);
      } else {
        lockedChart.clear();
      }

      const option = {
        tooltip: {
          formatter: params => `
        <div style="padding:5px;">
          <strong>${params.data.name || '未知节点'}</strong><br/>
          当前值: ${parseFloat(params.data.indicatorValue || 0).toFixed(2)}<br/>
          变化率: ${params.data.possibility >= 0 ? '+' : ''}${parseFloat(params.data.possibility || 0).toFixed(2)}%<br/>
          变化: ${parseFloat(params.data.previousValue || 0).toFixed(2)} → ${parseFloat(params.data.indicatorValue || 0).toFixed(2)}
        </div>`,
          extraCssText: 'text-align: left;'
        },
        series: [{
          type: 'graph',
          layout: 'force',
          edgeSymbol: ['none', 'arrow'],
          edgeSymbolSize: [4, 10],
          force: {
            repulsion: 500,
            edgeLength: [50, 150],
            gravity: 0.05
          },
          focusNodeAdjacency: true,
          // data: nodesToRender.map(node => ({
          //   id: node.id || `placeholder-${Math.random()}`,
          //   name: node.name || '未知节点',
          //   symbolSize: node.symbolSize || 50,
          //   itemStyle: { color: this.categories[node.category]?.color || '#1E90FF' },
          //   category: node.category || 0,
          //   indicatorValue: node.indicatorValue || 0,
          //   possibility: node.possibility || 0,
          //   previousValue: node.previousValue || 0
          // })),
          data: nodesToRender,
          links: linksToRender.map(link => ({
            source: link.source || '',
            target: link.target || '',
            rate: link.rate || 0
          })),
          categories: this.categories,
          roam: true,
          label: {
            show: true,
            color: '#333',
            fontSize: 12,
            position: 'right',
            formatter: params => params.name.length > 10 ? params.name.substring(0, 10) + '...' : params.name
          },
          lineStyle: {
            color: 'source',
            curveness: 0.3,
            width: 1,
            opacity: 0.7
          },
          emphasis: {
            focus: 'adjacency',
            lineStyle: { width: 3, color: '#FF4500' },
            label: { show: true }
          },
          unfocusEffect: { enabled: true, opacity: 0.2 }
        }]
      };

      this.$nextTick(() => {
        setTimeout(() => {
          try {
            lockedChart.setOption(option);
            lockedChart.resize();
            console.log('lockedRelationChart rendered successfully');
            // 渲染后强制更新图例
            this.renderCategoryLegend();
          } catch (error) {
            console.error('渲染锁定指标因果网络图失败:', error);
          }
        }, 100);
      });
    },

    updateDataByOrder() {
      console.log('updateDataByOrder called with selectedOrder:', this.selectedOrder);
      if (!this.taskViewMode) {
        console.warn('Not in task view mode, skipping update');
        return;
      }

      const task = this.activeTasks[this.currentTaskIndex];
      if (!task) {
        console.error('任务对象缺失，currentTaskIndex:', this.currentTaskIndex);
        alert('无法更新阶数数据：任务对象缺失，请返回主界面重试');
        this.switchTask(null);
        return;
      }

      console.log('Task data:', {
        taskName: task.name,
        evidenceData: task.evidenceData,
        initialLegendNodes: task.initialLegendNodes
      });

      if (!Array.isArray(task.evidenceData)) {
        console.warn('evidenceData 不是数组，初始化为空数组:', task.evidenceData);
        this.$set(task, 'evidenceData', []);
      }

      const selectedOrder = parseInt(this.selectedOrder, 10);
      if (isNaN(selectedOrder) || selectedOrder < 0 || selectedOrder > 3) {
        console.error('Invalid selectedOrder:', this.selectedOrder);
        return;
      }

      // 初始化 legendNodes 使用 legendData
      this.$set(this, 'legendNodes', JSON.parse(JSON.stringify(task.initialLegendNodes)).map(node => {
        const chineseName = NAME_MAPPING[node.originalName] || node.originalName;
        const originalValue = this.legendData[chineseName] ? parseFloat(this.legendData[chineseName]) : 0;
        return {
          ...node,
          indicatorValue: originalValue,
          previousValue: 0,
          originalValue,
          possibility: 0,
          userModified: false,
          isEvidence: false,
          propagationLevel: 0
        };
      }));

      // 处理各阶数据
      if (selectedOrder === 0) {
        const stage0Data = this.processEvidenceData(task.evidenceData, 0);
        stage0Data.forEach(data => {
          const node = this.legendNodes.find(n => n.originalName === data.originalName);
          if (node) {
            node.indicatorValue = data.indicatorValue;
            node.previousValue = data.previousValue;
            node.possibility = data.possibility;
            node.userModified = true;
            node.isEvidence = true;
            node.propagationLevel = 0;
          }
        });
      } else {
        // 覆盖 0 到 selectedOrder-1 阶的证据数据，标红
        for (let stage = 0; stage < selectedOrder; stage++) {
          const stageData = this.processEvidenceData(task.evidenceData, stage);
          stageData.forEach(data => {
            const node = this.legendNodes.find(n => n.originalName === data.originalName);
            if (node) {
              node.indicatorValue = data.indicatorValue;
              node.previousValue = data.previousValue;
              node.possibility = data.possibility;
              node.userModified = true;
              node.isEvidence = true;
              node.propagationLevel = 0;
            }
          });
        }

        // 覆盖当前阶的变化数据，标绿
        const stageData = this.processEvidenceData(task.evidenceData, selectedOrder);
        stageData.forEach(data => {
          const node = this.legendNodes.find(n => n.originalName === data.originalName);
          if (node) {
            node.indicatorValue = data.indicatorValue;
            node.previousValue = data.previousValue;
            node.possibility = data.possibility;
            node.userModified = false;
            node.isEvidence = false;
            node.propagationLevel = selectedOrder;
          }
        });
      }

      console.log('Updated legendNodes:', this.legendNodes);
      this.$forceUpdate();
    },

    toggleHiddenModule() {
      this.hiddenModuleVisible = !this.hiddenModuleVisible;
      if (this.hiddenModuleVisible) {
        this.$nextTick(() => {
          this.updateHiddenModuleCharts();
        });
      }
    },

    async updateHiddenModuleCharts() {
      const task = this.activeTasks[this.currentTaskIndex];
      if (!task || !task.slcData) {
        console.error('No SLC data available');
        alert('无法加载SLC数据');
        return;
      }

      this.processHiddenModuleFutureData(task.slcData);
      this.renderHiddenModuleCharts();
    },

    processHiddenModuleFutureData(futureDf1) {
      if (!futureDf1 || typeof futureDf1 !== 'object') {
        console.error('Invalid future_df_1 structure');
        this.hiddenDates = [];
        this.hiddenSeries = [];
        this.globalMin = 0;
        this.globalMax = 0;
        return;
      }

      this.hiddenDates = Object.keys(futureDf1).map(date => date.slice(0, 10)); // Convert to YYYY-MM-DD
      if (!this.hiddenDates || this.hiddenDates.length === 0) {
        console.error('No valid dates found in future_df_1');
        this.hiddenDates = [];
        this.hiddenSeries = [];
        this.globalMin = 0;
        this.globalMax = 0;
        return;
      }

      this.hiddenSeries = this.legendNodes.map(node => {
        return this.hiddenDates.map(date => {
          const value = futureDf1[date][node.originalName];
          return typeof value === 'number' && isFinite(value) ? value : null;
        });
      });

      let globalMin = Infinity;
      let globalMax = -Infinity;
      this.hiddenSeries.forEach(series => {
        const validValues = series.filter(val => val !== null);
        if (validValues.length > 0) {
          const min = Math.min(...validValues);
          const max = Math.max(...validValues);
          if (min < globalMin) globalMin = min;
          if (max > globalMax) globalMax = max;
        }
      });
      this.globalMin = isFinite(globalMin) ? globalMin : 0;
      this.globalMax = isFinite(globalMax) ? globalMax : 0;
    },

    renderHiddenModuleCharts() {
      const monthCount = parseInt(this.selectedForecastMonth, 10);
      if (isNaN(monthCount) || monthCount < 1 || monthCount > this.forecastMonth) {
        console.error('Invalid selectedForecastMonth:', this.selectedForecastMonth);
        return;
      }

      const filteredDates = this.hiddenDates.slice(0, monthCount);
      const filteredSeries = this.hiddenSeries.map(series => {
        if (!Array.isArray(series)) {
          console.warn('Invalid series data during filtering:', series);
          return [];
        }
        return series.slice(0, monthCount);
      });

      this.legendNodes.forEach((node, idx) => {
        const dom = document.getElementById("miniChart" + node.id);
        if (!dom) {
          console.warn(`Chart container not found: miniChart${node.id}`);
          return;
        }

        const chart = echarts.getInstanceByDom(dom) || echarts.init(dom);
        chart.clear();

        const seriesData = filteredSeries[idx] || [];
        if (seriesData.length === 0) {
          console.warn(`No data for indicator ${node.name} (index ${idx})`);
        }

        const validData = seriesData.filter(val => typeof val === 'number' && isFinite(val));
        let chartMin = validData.length > 0 ? Math.min(...validData) : 0;
        let chartMax = validData.length > 0 ? Math.max(...validData) : 0;

        const range = chartMax - chartMin;
        const margin = range * 0.1 || Math.abs(chartMax) * 0.1 || 1;
        chartMin = chartMin < 0 ? chartMin - margin : Math.min(chartMin - margin, 0);
        chartMax = chartMax > 0 ? chartMax + margin : Math.max(chartMax + margin, 0);

        const precision = Math.max(0, Math.floor(Math.log10(Math.abs(range))) - 1);
        chartMin = Math.floor(chartMin / Math.pow(10, precision)) * Math.pow(10, precision);
        chartMax = Math.ceil(chartMax / Math.pow(10, precision)) * Math.pow(10, precision);

        const tickCount = 5;
        const interval = Math.ceil((chartMax - chartMin) / tickCount / Math.pow(10, precision)) * Math.pow(10, precision);

        chart.setOption({
          tooltip: { trigger: "axis" },
          xAxis: {
            type: "category",
            data: filteredDates,
            axisLabel: {
              rotate: 45,
              fontSize: 7,
              interval: 0
            }
          },
          yAxis: {
            type: "value",
            min: chartMin,
            max: chartMax,
            interval: interval,
            axisLabel: {
              fontSize: 8,
              formatter: function (value) {
                return Number(value).toFixed(Math.max(0, 2 - Math.floor(Math.log10(Math.abs(value) || 1))));
              }
            }
          },
          series: [{
            data: seriesData,
            type: "line",
            smooth: true,
            symbol: "circle",
            symbolSize: 4,
            lineStyle: { width: 2 }
          }],
          grid: { left: "15%", right: "5%", top: "15%", bottom: "25%" }
        });
      });
    },

    async initializeLineCharts() {
      if (!this.taskViewMode) return;
      const task = this.activeTasks[this.currentTaskIndex];
      if (!task || !task.predictionData) {
        console.error('No prediction data available');
        alert('无法加载折线图数据');
        return;
      }
      this.renderLineCharts(task.predictionData);
    },

    renderLineCharts(predictionData) {
      console.log('Rendering line charts with predictionData:', predictionData);

      if (!predictionData || typeof predictionData !== 'object') {
        console.warn('predictionData 无效，显示空图表');
        predictionData = {};
      }

      const task = this.activeTasks[this.currentTaskIndex];
      if (!task) {
        console.error('任务对象缺失，currentTaskIndex:', this.currentTaskIndex);
        return;
      }

      const maxRisk = predictionData.macro_sim_pre_max_risk
          ? Object.entries(predictionData.macro_sim_pre_max_risk)
              .map(([date, value]) => ({ date: date.slice(0, 10), GBRT: parseFloat(value) || 0 }))
              .filter(item => item.date && isFinite(item.GBRT))
          : [];
      const meanRisk = predictionData.macro_sim_pre_mean_risk
          ? Object.entries(predictionData.macro_sim_pre_mean_risk)
              .map(([date, value]) => ({ date: date.slice(0, 10), GBRT: parseFloat(value) || 0 }))
              .filter(item => item.date && isFinite(item.GBRT))
          : [];
      const meanIndex = predictionData.macro_sim_pre_mean_index
          ? Object.entries(predictionData.macro_sim_pre_mean_index)
              .map(([date, value]) => ({ date: date.slice(0, 10), value: parseFloat(value) || 0 }))
              .filter(item => item.date && isFinite(item.value))
          : [];
      const minIndex = predictionData.macro_sim_pre_min_index
          ? Object.entries(predictionData.macro_sim_pre_min_index)
              .map(([date, value]) => ({ date: date.slice(0, 10), value: parseFloat(value) || 0 }))
              .filter(item => item.date && isFinite(item.value))
          : [];

      console.log('Processed data:', { maxRisk, meanRisk, meanIndex, minIndex });

      // 使用 meanIndex 优先的日期对齐逻辑
      const dates = meanIndex.length > 0
          ? meanIndex.map(item => item.date)
          : minIndex.length > 0
              ? minIndex.map(item => item.date)
              : maxRisk.length > 0
                  ? maxRisk.map(item => item.date)
                  : meanRisk.length > 0
                      ? meanRisk.map(item => item.date)
                      : ['无数据'];

      const maxVals = maxRisk.map(item => item.GBRT);
      const meanVals = meanRisk.map(item => item.GBRT);
      const meanIndexVals = meanIndex.map(item => item.value);
      const minIndexVals = minIndex.map(item => item.value);

      const dom1 = document.getElementById('lineChart1');
      if (!dom1) {
        console.error('未找到 #lineChart1 容器');
        return;
      }

      let chart1 = echarts.getInstanceByDom(dom1);
      if (!chart1) {
        chart1 = echarts.init(dom1);
      } else {
        chart1.clear();
      }

      chart1.setOption({
        title: { text: '系统性风险预测', left: 'center' },
        tooltip: { trigger: 'axis' },
        legend: { data: ['系统性风险最大值', '系统性风险平均值'], top: '5%',
          orient: 'horizontal',
          itemWidth: 20, // 调整图例项宽度
          itemHeight: 14,
          textStyle: { fontSize: 12 } },

        xAxis: {
          type: 'category',
          data: dates,
          axisLabel: { rotate: 45 }
        },
        yAxis: { type: 'value' },
        series: [
          {
            name: '系统性风险最大值',
            type: 'line',
            data: maxVals.length > 0 ? maxVals : [0],
            smooth: true,
            symbol: 'circle',
            symbolSize: 6,
            lineStyle: { color: 'red', width: 2 }
          },
          {
            name: '系统性风险平均值',
            type: 'line',
            data: meanVals.length > 0 ? meanVals : [0],
            smooth: true,
            symbol: 'circle',
            symbolSize: 6,
            lineStyle: { color: 'blue', width: 2 }
          }
        ],
        grid: { left: '3%', right: '4%', top:'20%',bottom: '15%', containLabel: true }
      });

      const dom2 = document.getElementById('lineChart2');
      if (!dom2) {
        console.error('未找到 #lineChart2 容器');
        return;
      }
      let chart2 = echarts.getInstanceByDom(dom2);
      if (!chart2) {
        chart2 = echarts.init(dom2);
      } else {
        chart2.clear();
      }

      chart2.setOption({
        title: { text: '上证指数预测', left: 'center' },
        tooltip: { trigger: 'axis' },
        legend: { data: ['上证指数平均值', '上证指数最小值'], top: '5%',
          orient: 'horizontal',
          itemWidth: 20,
          itemHeight: 14,
          textStyle: { fontSize: 12 } },

        xAxis: {
          type: 'category',
          data: dates,
          axisLabel: { rotate: 45 }
        },
        yAxis: { type: 'value' },
        series: [
          {
            name: '上证指数平均值',
            type: 'line',
            data: meanIndexVals.length > 0 ? meanIndexVals : [0],
            smooth: true,
            symbol: 'circle',
            symbolSize: 6,
            lineStyle: { color: 'green', width: 2 }
          },
          {
            name: '上证指数最小值',
            type: 'line',
            data: minIndexVals.length > 0 ? minIndexVals : [0],
            smooth: true,
            symbol: 'circle',
            symbolSize: 6,
            lineStyle: { color: 'purple', width: 2 }
          }
        ],
        grid: { left: '3%', right: '4%', top: '20%', bottom: '15%', containLabel: true }
      });

      this.$nextTick(() => {
        chart1.resize();
        chart2.resize();
        console.log('Line charts resized');
      });
    }
  }
};

</script>

<style scoped>
body {
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  background-color: #f9f9f9;
  margin: 0;
  padding: 0;
}


#legendActions button:hover {
  background-color: #45A049;
}

#legendActions button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

#container {
  width: 95%;
  max-width: 1700px;
  margin: 30px auto;
  background-color: white;
  border-radius: 12px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
  padding: 20px;
  position: relative;
  overflow: auto;
}

#taskContainer {
  position: relative;
  width: 100%;
  padding: 10px;
  background-color: #f0f0f0;
  border-bottom: 1px solid #ccc;
  margin-bottom: 20px;
}

#taskTabs {
  display: flex;
  justify-content: flex-start;
  gap: 10px;
}

.task-tab {
  padding: 4px 8px 4px 8px; /* 调整内边距以适应叉号 */
  background-color: #ddd;
  cursor: pointer;
  border-radius: 4px;
  position: relative;
  user-select: none;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 5px;
}

.delete-icon {
  color: #FF4500;
  font-size: 16px;
  cursor: pointer;
  padding: 2px 5px;
  border-radius: 50%;
  transition: background-color 0.2s;
}

.delete-icon:hover {
  background-color: #ffe6e6;
}

.history-task-item {
  font-size: 12px;
  margin-bottom: 3px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 2px 5px;
}

.history-task-item:hover {
  background-color: #e0e0e0;
}

.delete-confirm {
  position: absolute;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  z-index: 1001;
  box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
  padding: 10px;
  width: 220px; /* 略微加宽以适应中文提示 */
  max-width: 90vw; /* 响应式调整 */
}

.task-tab.active {
  background-color: #FF4500;
  color: #fff;
  font-weight: bold;
}

.flash {
  animation: flashHighlight 1s infinite;
}

@keyframes flashHighlight {
  0% { background-color: #FF4500; }
  50% { background-color: #ff6666; }
  100% { background-color: #FF4500; }
}

.context-menu {
  position: absolute;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  z-index: 1000;
  box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
}

.context-menu-item {
  padding: 5px 10px;
  cursor: pointer;
  font-size: 14px;
}

.context-menu-item:hover {
  background-color: #f0f0f0;
}


.delete-confirm-message {
  font-size: 14px;
  margin-bottom: 10px;
  color: #333;
}

.delete-confirm-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.delete-confirm-buttons button {
  padding: 5px 10px;
  font-size: 12px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.delete-confirm-buttons button:first-child {
  background-color: #FF4500;
  color: white;
}

.delete-confirm-buttons button:first-child:hover {
  background-color: #e03e00;
}

.delete-confirm-buttons button:last-child {
  background-color: #ccc;
  color: #333;
}

.delete-confirm-buttons button:last-child:hover {
  background-color: #bbb;
}

#historyContainer {
  position: absolute;
  top: 10px;
  right: 10px;
  text-align: right;
}

#toggleHistory {
  padding: 4px 8px;
  font-size: 12px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

#toggleHistory:hover {
  background-color: #45A049;
}

#historyTaskList {
  margin-top: 5px;
  background-color: #f5f5f5;
  border: 1px solid #ccc;
  padding: 8px;
  border-radius: 6px;
  max-height: 300px;
  overflow-y: auto;
}

#historyTaskList h3 {
  margin: 0 0 5px 0;
  font-size: 14px;
}

#historyTaskList ul {
  list-style: none;
  padding: 0;
  margin: 0;
}


#historyTaskList li:hover {
  background-color: #e0e0e0;
}

#monthInputLegend {
  margin-bottom: 15px;
  text-align: left;
}

#monthInputLegend label {
  font-size: 14px;
  margin-right: 8px;
  color: #333;
}

#monthInputLegend input {
  width: 60px;
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
}

#relationSection,
#lockedRelationSection {
  margin-bottom: 20px;
  border: 1px solid #e0e0e0;
  padding: 15px;
  border-radius: 8px;
  background-color: #fafafa;
}

#edgeControls {
  margin-bottom: 15px;
  padding: 10px;
  border-radius: 6px;
  background-color: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.edge-control-wrapper {
  display: flex;
  flex-wrap: wrap;
  gap: 15px;
  justify-content: space-between;
}

.edge-add,
.edge-remove {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 400px;
}

.edge-add label,
.edge-remove label {
  font-size: 14px;
  color: #333;
  font-weight: 500;
}

.edge-add select,
.edge-remove select {
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
  flex: 1;
  min-width: 150px;
}

.edge-add span,
.edge-remove span {
  font-size: 16px;
  color: #666;
}

.edge-add button,
.edge-remove button {
  padding: 6px 12px;
  font-size: 14px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.edge-add button:hover,
.edge-remove button:hover {
  background-color: #45A049;
}

#lock-relation,
.relation-actions button {
  padding: 8px 16px;
  font-size: 14px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: background-color 0.3s;
}

#lock-relation:hover,
.relation-actions button:hover {
  background-color: #45A049;
}

#unlock-relation:disabled,
#reset-network:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.relation-actions {
  margin-top: 10px;
  display: flex;
  gap: 10px;
}

#topLegend,
#taskLegend {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-template-rows: repeat(5, auto);
  gap: 8px;
  padding: 10px;
  border: 1px solid #e0e0e0;
  margin-bottom: 10px;
}

.legend-item {
  display: grid;
  grid-template-columns: repeat(2, auto);
  grid-template-rows: repeat(2, auto);
  gap: 4px;
  background-color: #f0f8ff;
  padding: 6px;
  border-radius: 6px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.legend-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: #555;
  font-weight: 400;
}

.legend-cell.input-wrapper {
  display: flex;
  align-items: center;
  position: relative;
}

.legend-cell input {
  width: 50px;
  padding: 4px;
  border: 1px solid #ccc;
  border-radius: 4px;
  text-align: center;
  font-size: 12px;
}

input.no-spinners::-webkit-inner-spin-button,
input.no-spinners::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

input.no-spinners[type="number"] {
  -moz-appearance: textfield;
}

.spinner-buttons {
  display: flex;
  flex-direction: column;
  margin-left: 5px;
}

.spinner-up,
.spinner-down {
  width: 20px;
  height: 12px;
  line-height: 12px;
  font-size: 10px;
  padding: 0;
  border: 1px solid #ccc;
  background-color: #f9f9f9;
  cursor: pointer;
  text-align: center;
  user-select: none;
}

.spinner-up {
  border-bottom: none;
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
}

.spinner-down {
  border-bottom-left-radius: 3px;
  border-bottom-right-radius: 3px;
}

.spinner-up:hover,
.spinner-down:hover {
  background-color: #e0e0e0;
}

.spinner-up:disabled,
.spinner-down:disabled {
  background-color: #f0f0f0;
  cursor: not-allowed;
  color: #aaa;
}

.red-text {
  color: red !important;
  font-weight: bold;
}

.green-text {
  color: green !important;
  font-weight: bold;
}

#legendActions {
  margin-top: 10px;
  text-align: right;
}

#legendActions button {
  padding: 4px 8px;
  font-size: 14px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
  margin-right: 10px;
}


#hidden-module {
  border: 1px solid #e0e0e0;
  padding: 15px;
  border-radius: 8px;
  background-color: #fafafa;
  margin-top: 10px;
}

#hidden-module-header {
  margin-bottom: 10px;
}

#hidden-module-header label {
  font-size: 14px;
  margin-right: 8px;
  color: #333;
}

#hidden-module select {
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
}

#data-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 5px;
}

.current-task {
  background-color: #FFD700 !important; /* 金黄色，突出当前任务 */
  color: #333 !important; /* 确保文字可读 */
  font-weight: bold;
  border: 2px solid #FFA500; /* 橙色边框进一步区分 */
}

.current-task:hover {
  background-color: #FFC107 !important; /* 鼠标悬停时加深颜色 */
}

.grid-item {
  padding: 6px;
  background-color: #eef;
  text-align: center;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 12px;
}

#lineChart-controls {
  margin-bottom: 10px;
  text-align: left;
}

#lineChart-controls label {
  font-size: 14px;
  margin-right: 8px;
  color: #333;
}

#lineChart-controls select {
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
}

#lineChart-container {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 20px;
}

#lineChart1,
#lineChart2 {
  width: 48%;
  height: 400px;
}

#orderControls {
  margin-bottom: 10px;
  text-align: left;
}

#orderControls label {
  font-size: 14px;
  margin-right: 8px;
  color: #333;
}

#orderControls select {
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
}

@media (max-width: 768px) {
  #relationChart,
  #lockedRelationChart {
    height: 400px;
  }

  .edge-add,
  .edge-remove {
    min-width: 100%;
    flex-direction: column;
    align-items: flex-start;
  }

  .edge-add select,
  .edge-remove select {
    width: 100%;
    margin-bottom: 5px;
  }

  .edge-add span,
  .edge-remove span {
    margin: 5px 0;
  }

  .edge-add button,
  .edge-remove button {
    width: 100%;
  }

  #topLegend,
  #taskLegend {
    grid-template-columns: repeat(4, 1fr);
  }

  .legend-item {
    grid-template-columns: 1fr;
    grid-template-rows: repeat(4, auto);
  }

  #data-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  #lineChart-container {
    flex-direction: column;
    gap: 10px;
  }

  #categoryLegend {
    display: flex;
    justify-content: flex-start;
    gap: 20px;
    margin-bottom: 15px;
    padding: 10px;
    background-color: #f5f5f5;
    border-radius: 6px;
    overflow: visible; /* 确保不隐藏子元素 */
    z-index: 10; /* 提高层级，避免被 ECharts 覆盖 */
  }

  .category-legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 24px; /* 确保有足够空间显示色块 */
  }

  .legend-color {
    width: 16px !important;
    height: 16px !important;
    border-radius: 50%;
    display: inline-block;
    background-color: inherit !important; /* 强制继承 :style 的颜色 */
    flex-shrink: 0; /* 防止色块被压缩 */
    border: 1px solid #ccc; /* 增强可见性 */
    vertical-align: middle; /* 垂直对齐 */
  }

  .legend-text {
    font-size: 14px;
    color: #333;
    vertical-align: middle; /* 垂直对齐 */
  }

  @media (max-width: 768px) {
    #categoryLegend {
      flex-wrap: wrap;
      gap: 10px;
    }

    .category-legend-item {
      flex: 0 0 auto;
      align-items: center;
    }
  }

  #lineChart1,
  #lineChart2 {
    width: 100%;
    height: 300px;
  }
}
</style>